
import { emptyObject, getType, toKebabCase, isEmptyObject } from "utils-lib-js"
import { ANSIParams } from "./static.js"
import { IOpts, IGlobalOpts, IColorMode, IColorType, IRGB, IColor, ITextStyle, IMoveParams, IColorStr, ICursor } from "./types"
// 浏览器中把下面的放开
// const { getType, emptyObject, toKebabCase, isEmptyObject } = window.UtilsLib

// ANSI 类，用于生成 ANSI 控制码
class ANSI {
    // moveTo 用于生成移动光标的 ANSI 控制码
    moveTo = (opts: IMoveParams) => {
        const { direct, position } = opts ?? {}
        if (!direct || !position) return ``
        let _p = ``
        if (direct === "custom" && typeof position === "object") {
            const { col, row } = position
            _p = `${row};${col}`
        } else if (typeof position !== "object") {
            _p = `${position}`
        }
        if (_p) return `${_p}${ANSIParams.cursor.direct[direct] ?? ""}`
        return ``
    }
    // scrollTo 方法，用于生成滚动屏幕的 ANSI 控制码
    scrollTo = (row: number) => {
        if (!row) return ``
        // 滚动条向下滚动，代码对应向上移动一行，相当于删除（backspace）row行
        let direct = ANSIParams.scroll.down
        if (row < 0) {
            // 滚动条向上滚动，代码对应向下移动一行，相当于回车（enter）row行
            direct = ANSIParams.scroll.up
        }
        return `${Math.abs(row)}${direct}`
    }
    // 生成光标相关的 ANSI 控制码
    getCursor = (cursor: ICursor) => {
        if (cursor) return `${ANSIParams.cursor[cursor] ?? ""}`
        else return ``
    }
    // 生成颜色和样式相关的 ANSI 控制码
    getColorMode = (mode: IColorMode) => `${ANSIParams.colorMode[mode] ?? "2"}`
    getColorType = (type: IColorType) => `${ANSIParams.colorType[type] ?? "38"}`
    getRGB = (options: Partial<IRGB>) => {
        const { type = "foreground", mode = "trueColor", red = 0, green = 0, blue = 0, color = 0 } = options
        const cType = this.getColorType(type), cMode = this.getColorMode(mode)
        if (cMode === ANSIParams.colorMode.compatibility) return `${cType};${cMode};${color}`
        return `${cType};${cMode};${red};${green};${blue}`
    }
    getColor = (color: IColorStr) => {
        if (color) return `${ANSIParams.color[color] ?? ""}`
        else return ``
    }
    getTextStyle = (textStyle: ITextStyle) => {
        if (textStyle) return `${ANSIParams.textStyle[textStyle] ?? ""}`
        else return ``
    }
    // 生成最终的 ANSI 控制码字符
    getANSI = (params: string, isStyle: boolean = true) => params ? `\x1B[${params}${isStyle ? "m" : ""}` : ""
    // 重置所有样式
    reset = () => this.getANSI(`0`)
}
const defaultOpts: IGlobalOpts = {
    reset: true,
    type: "log",
    split: true
}
// JSLog 类，继承 ANSI 类，用于生成日志，并根据环境打印到控制台或执行其他操作
export class JSLog extends ANSI {
    readonly stylePlaceholder = "%c"; // 浏览器环境下字符串占位符：%c
    isNode: boolean = typeof process !== "undefined";
    private globalOpts: IGlobalOpts = defaultOpts
    // 全局选项作为参数
    constructor(globalOpts?: IGlobalOpts) {
        super()
        this.checkOptions(globalOpts)
        this.mixins(globalOpts)
    }
    // 合并全局选项
    mixins(opts: IGlobalOpts) {
        this.globalOpts = emptyObject({ ...this.globalOpts, ...opts })
        return this.globalOpts
    }
    // 清空全局样式及行为
    clear() {
        this.globalOpts = emptyObject(defaultOpts)
    }
    // 生成日志
    log = (...args: IOpts[]) => {
        if (args.length <= 0) return this
        const { logQueue, styleQueue } = this.createQueue(args)
        this.logFn(logQueue, styleQueue)
        return this
    }
    // 生成日志队列
    private createQueue = (args: IOpts[]) => {
        const styleQueue = [], logQueue: string[] = []
        const { isNode, stylePlaceholder, globalOpts } = this
        args.forEach(it => {
            this.checkOptions(it)
            const { text = "" } = it
            const _opts = emptyObject({ ...globalOpts, ...it })
            const { reset, style = {} } = _opts
            const hasStyle = !isEmptyObject(style)
            logQueue.push(`${this.formate(_opts)}${hasStyle ? stylePlaceholder : ""}${text}${reset ? this.reset() : ""}`)
            if (hasStyle) {
                const _style = this.formatStyle(style)
                _style && styleQueue.push(_style)
            }
        })
        if (isNode && globalOpts.split) {
            return { logQueue, styleQueue }
        }
        return { logQueue: logQueue.join(""), styleQueue }
    }
    // 格式化选项生成 ANSI 控制码
    private formate(opts: IOpts) {
        const { color, textStyle, cursor, move, scroll } = opts
        // TODO:控制渲染执行顺序
        const params = this.getANSI(`${this.formatCursor(cursor)}${this.moveTo(move)}${this.scrollTo(scroll)}`, false) + this.getANSI(`${this.formatColor(color)}${this.formatText(textStyle)}`)
        return params
    }
    // formatParams、formatText、formatColor方法，格式化样式和颜色选项
    private formatCursor(cursor: ICursor) {
        if (typeof cursor === "string") {
            return this.getCursor(cursor)
        }
        return ``
    }
    private formatText(textStyle: ITextStyle[] | ITextStyle) {
        if (typeof textStyle === "string") {
            return this.getTextStyle(textStyle)
        } else if (typeof textStyle === "object") {
            let __temp = ``;
            textStyle.forEach(it => __temp += this.getTextStyle(it))
            return __temp
        }
        return ``
    }
    private formatColor(color: IColor[] | IColor) {
        if (typeof color === "string") {
            return this.getColor(color)
        } else if (typeof color === "object") {
            if (getType(color) === "array") {
                let __temp = ``;
                (color as IColor[]).forEach(it => __temp += this.formatColor(it))
                return __temp
            } else {
                return this.getRGB(color as IRGB)
            }
        }
        return ``
    }
    // 格式化浏览器端的style样式属性
    private formatStyle(styles: Partial<CSSStyleDeclaration>) {
        let styleStr = ``
        Reflect.ownKeys(styles).forEach(k => {
            if (typeof k === "string") styleStr += `${toKebabCase(k)}:${styles[k]};`
        })
        return styleStr
    }
    // 执行打印操作
    private logFn(logQueue: string[] | string, styleQueue: string[]) {
        const { globalOpts } = this
        const { type } = globalOpts
        if (typeof logQueue === "string") {
            return console[type](logQueue, ...styleQueue)
        }
        return console[type](...logQueue, ...styleQueue)
    }
    // 校验options参数
    checkOptions(opts: IGlobalOpts = {}) {
        const { isNode } = this
        const { cursor, move, scroll, style, color, textStyle } = opts
        if (isNode && style) throw Error("node环境下无法使用style相关属性")
        if (!isNode && (cursor || move || scroll)) throw Error("window环境下无法使用光标相关属性")
        if (!isNode && style && (color || textStyle)) console.warn("请注意：样式可能会冲突")
    }
}
export default JSLog


